package com.btl.component.redis;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
 * RedisTemplate
 */
@EnableCaching
@Configuration
public class RedisTemplateConfig {

    @Bean
    public ObjectMapper mapper() {
        ObjectMapper mapper = JsonMapper.builder()
                .disable(MapperFeature.REQUIRE_HANDLERS_FOR_JAVA8_TIMES)
                .build();
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        // 序列化规则
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        // 反序列化规则
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 启用类型信息注入（关键配置）
        mapper.activateDefaultTyping(
                LaissezFaireSubTypeValidator.instance,  // 类型验证器（宽松模式）
                ObjectMapper.DefaultTyping.NON_FINAL,   // 对非final类添加类型信息
                JsonTypeInfo.As.PROPERTY                // 类型信息以字段形式存储（如@class）
        );
        mapper.registerModule(new JavaTimeModule());
        return mapper;
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();

        redisTemplate.setConnectionFactory(redisConnectionFactory);
        // Key 序列化
        redisTemplate.setKeySerializer(RedisSerializer.string());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer(mapper()));
        // Hash 类型序列化
        redisTemplate.setHashKeySerializer(RedisSerializer.string());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer(mapper()));
        return redisTemplate;
    }

    @Bean
    public RedisCacheManager redisCacheManager(RedisConnectionFactory connectionFactory) {
        // 1. 创建 RedisCacheWriter（默认实现）
        RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
        // 2. 定义默认的 RedisCacheConfiguration
        RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
                // 序列化方式：JSON 格式（避免 JDK 序列化的二进制不可读性）
                .serializeValuesWith(
                        RedisSerializationContext.SerializationPair.fromSerializer(
                                new GenericJackson2JsonRedisSerializer(mapper())
                        )
                )
                // 全局缓存默认过期时间：30 分钟
                .entryTtl(Duration.ofMillis(60))
                // 是否允许缓存空值（防止缓存穿透）
//                .disableCachingNullValues()
                // 缓存 Key 的前缀（可选）
                .prefixCacheNameWith(GlobalRedisConstant.GLOBAL_REDIS_KEY + ":translate:cache:");

        // 3. 创建 RedisCacheManager 并关联配置
        return RedisCacheManager.builder(cacheWriter)
                .cacheDefaults(defaultConfig)
                // 可选：为特定缓存名指定独立配置（覆盖默认配置）
                .withInitialCacheConfigurations(getCustomCacheConfigs())
                // 启用事务支持（确保缓存操作与事务同步）
                .transactionAware()
                .build();
    }

    /**
     * 定义特定缓存的个性化配置（例如不同缓存不同 TTL）
     */
    private Map<String, RedisCacheConfiguration> getCustomCacheConfigs() {
        Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
        configMap.put("route", RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(12))
                .prefixCacheNameWith(GlobalRedisConstant.GLOBAL_REDIS_KEY + ":common:cache:translate:")
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(
                        new GenericJackson2JsonRedisSerializer(mapper())
                ))
        );
        configMap.put("setting", RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(12))
                .prefixCacheNameWith(GlobalRedisConstant.GLOBAL_REDIS_KEY + ":common:cache:config:")
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(
                        new GenericJackson2JsonRedisSerializer(mapper())
                ))
        );
        configMap.put("latest", RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(12))
                .prefixCacheNameWith(GlobalRedisConstant.GLOBAL_REDIS_KEY + ":common:cache:version:")
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(
                        new GenericJackson2JsonRedisSerializer(mapper())
                ))
        );
        configMap.put("phone", RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(12))
                .prefixCacheNameWith(GlobalRedisConstant.GLOBAL_REDIS_KEY + ":common:cache:area:")
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(
                        new GenericJackson2JsonRedisSerializer(mapper())
                ))
        );
        configMap.put("type", RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(12))
                .prefixCacheNameWith(GlobalRedisConstant.GLOBAL_REDIS_KEY + ":common:cache:package:")
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(
                        new GenericJackson2JsonRedisSerializer(mapper())
                ))
        );
        configMap.put("freeLimit", RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(12))
                .prefixCacheNameWith(GlobalRedisConstant.GLOBAL_REDIS_KEY + ":common:cache:package:")
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(
                        new GenericJackson2JsonRedisSerializer(mapper())
                ))
        );
        return configMap;
    }

}
